﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using NeuralNetwork.Layers;

namespace NeuralNetwork.Neurons
{
    public class Axon
    {        
        private double oldWeight = 1;
        private double weight = 1;
        private Neuron from = null;
        private Neuron to = null;

        public Axon()
        { 
        
        }

        public Axon(Neuron from, Neuron to, double weight)
        {
            this.from = from;
            this.to = to;
            this.weight = weight;
            this.oldWeight = weight;
        }

        public Neuron From
        {
            get { return from; }
        }

        public Neuron To
        {
            get { return to; }
        }

        public double Weight
        {
            get 
            { 
                return weight; 
            }
            set 
            {
                oldWeight = weight;
                weight = value;
            }
        }

        public void AdjustWeight(double delta)
        {            
            Weight = weight + delta;            
        }

        public void Absorb(double charge)
        {
            to.Absorb(weight * charge);            
        }
        
        public void Revert()
        {
            weight = oldWeight;
        }

        public bool Prune(double limit)
        {
            return ((Math.Abs(weight) < limit) && (Math.Abs(oldWeight) < limit));
        }

        virtual public XmlNode Save(XmlDocument doc, XmlNode parent)
        {
            Network network = null;
            if (to != null) network = to.network;
            else if (from != null) network = from.network;
            if (network == null) return null;

            XmlNode node = network.AddChildToNode(doc, parent, this.GetType().ToString().Remove(0, 14), null);
            if (to != null) network.AddChildToNode(doc, node, "to", to.ID.ToString());
            if (from != null) network.AddChildToNode(doc, node, "from", from.ID.ToString());
            network.AddChildToNode(doc, node, "weight", weight.ToString());

            return node;
        }

        private Neuron findNeuron(Network network, string id)
        {
            foreach (Layer l in network.layers)
                foreach (Neuron n in l.neurons)
                    if (n.ID == id) return n;
            
            return null;
        }

        virtual public void Load(Network network, XmlNode axonNode)
        {
            // Get Layer data 
            foreach (XmlNode n in axonNode.ChildNodes)
            {
                switch (n.Name.ToUpper())
                {
                    case "WEIGHT":
                        weight = Convert.ToDouble(n.InnerText.Trim());
                        oldWeight = weight;
                        break;

                    case "TO":
                        to = findNeuron(network, n.InnerText.Trim());
                        if (to != null) to.dendrites.Add(this);
                        break;

                    case "FROM":
                        from = findNeuron(network, n.InnerText.Trim());
                        if (from != null) from.axons.Add(this);
                        break;

                    default:
                        //ignore
                        break;
                }
            }
        }
    }
}
